/*!
 * \file set_relation_test_spmv.cc
 *
 * \brief Set and Relation tests from operations performed by the Python
 * implementation of the spmv spec file and applying transformations up
 * to and including the sparse loop optimization.
 *
 * \date Started: 12/14/2010
 * \date Last Modified: 12/14/2010
 * \authors Alan LaMielle
 */

#include <iegenlib/iegenlib.h>

#include <gtest/gtest.h>

using iegenlib::Set;
using iegenlib::Relation;

TEST(SPMVRealTest,set_union_tests){
   //These tests were taken from a full run of the Python version of IEGen using
   //the spmv computation applying transformations up to sparse loop
   vector<string> union_in1s,union_in2s,expected_results;

   //Current spmv+cache blocking does not use union

   //Make sure there are equal numbers of inputs
   ASSERT_EQ(union_in1s.size(),union_in2s.size());
   ASSERT_EQ(union_in2s.size(),expected_results.size());

   for(int i=0; i<union_in1s.size(); i++) {
      Set union_in1(union_in1s[i]);
      Set union_in2(union_in2s[i]);
      Set result(union_in1.Union(union_in2));
      Set expected_result(expected_results[i]);

      EXPECT_EQ(result,expected_result) <<
         union_in1.get_isl() << endl << ".Union(" << union_in2.get_isl() <<
         ") !=" << expected_result.get_isl();
   }
}

TEST(SPMVRealTest,relation_union_tests){
   //These tests were taken from a full run of the Python version of IEGen using
   //the spmv computation applying transformations up to sparse loop
   vector<string> union_in1s,union_in2s,expected_results;

   //Current spmv+cache blocking does not use union

   //Make sure there are equal numbers of inputs
   ASSERT_EQ(union_in1s.size(),union_in2s.size());
   ASSERT_EQ(union_in2s.size(),expected_results.size());

   for(int i=0; i<union_in1s.size(); i++) {
      Relation union_in1(union_in1s[i]);
      Relation union_in2(union_in2s[i]);
      Relation result(union_in1.Union(union_in2));
      Relation expected_result(expected_results[i]);

      EXPECT_EQ(result,expected_result) <<
         union_in1.get_isl() << endl << ".Union(" << union_in2.get_isl() <<
         ") !=" << expected_result.get_isl();
   }
}

TEST(SPMVRealTest,apply_tests){
   //These tests were taken from a full run of the Python version of IEGen using
   //the spmv computation applying transformations up to sparse loop
   vector<string> apply_set_ins,apply_rel_ins,expected_results;

   apply_set_ins.push_back("[nnz]->{[i] : nnz-i>0 and i>=0}");
                 // [ncols,nnz,nrows]->{[i]: i>=0 and nnz>=1+i}
   apply_rel_ins.push_back("{[i]->[0,i,0] }");
   expected_results.push_back("[nnz]->{[0,i0,0] : nnz-i0>0 and i0>=0}"); 

   apply_set_ins.push_back("[nnz]->{[0,i0,0] : nnz-i0>0 and i0>=0}");
   apply_rel_ins.push_back("{[0,i,0]->[0,b,0,r,2,i,0] : r-row(i)=0 and b-cb(i)=0}");
   expected_results.push_back("[nnz]->{[0,b,0,r,2,i0,0] : r-row(i0)=0 and b-cb(i0)=0 and nnz-i0>0 and i0>=0}");

   apply_set_ins.push_back("[nnz]->{[0,b,0,r,0,i0,0] : r-row(i0)=0 and b-cb(i0)=0 and nnz-i0>0 and i0>=0}");
   apply_rel_ins.push_back("{[0,b,0,r,0,i0,0]->[b,r,i0] }");
   expected_results.push_back("[nnz]->{[b0,r0,i00] : r0-row(i00)=0 and b0-cb(i00)=0 and nnz-i00>0 and i00>=0}");

   apply_set_ins.push_back("[nnz]->{[0,b,0,r,0,i0,0] : r-row(i0)=0 and b-cb(i0)=0 and nnz-i0>0 and i0>=0}");
   apply_rel_ins.push_back("{[0,b0,0,r0,0,i00,0]->[b0,r0,i00] }");
   expected_results.push_back("[nnz]->{[b,r,i0] : r-row(i0)=0 and b-cb(i0)=0 and nnz-i0>0 and i0>=0}");

   apply_set_ins.push_back("{[s0]}");
   apply_rel_ins.push_back("{[i]->[0,b,0,r,0,i,0] : r-row(i)=0 and b-cb(i)=0}");
   expected_results.push_back("{[0,b,0,r,0,i0,0] : r-row(i0)=0 and b-cb(i0)=0}");

   //Make sure there are equal numbers of inputs
   ASSERT_EQ(apply_set_ins.size(),apply_rel_ins.size());
   ASSERT_EQ(apply_rel_ins.size(),expected_results.size());

   for(int i=0; i<apply_set_ins.size(); i++) {
      Set apply_set_in(apply_set_ins[i]);
      Relation apply_rel_in(apply_rel_ins[i]);
      Set result(apply_rel_in.Apply(apply_set_in));
      Set expected_result(expected_results[i]);

      EXPECT_EQ(result,expected_result) <<
         apply_rel_in.get_isl() << endl << ".Apply(" << apply_set_in.get_isl() <<
         ") !=" << expected_result.get_isl();
   }
}

TEST(SPMVRealTest,inverse_tests){
   //These tests were taken from a full run of the Python version of IEGen using
   //the spmv computation applying transformations up to sparse loop
   vector<string> inverse_rels,expected_results;

   inverse_rels.push_back("{[i]->[0,i,0]}");
   expected_results.push_back("{[0,i,0]->[i]}");

   inverse_rels.push_back("{[i]->[0,i,0]}");
   expected_results.push_back("{[0,i,0]->[i]}");

   inverse_rels.push_back("{[i]->[0,i,0]}");
   expected_results.push_back("{[0,i,0]->[i]}");

   inverse_rels.push_back("{[0,i,0]->[0,b,0,r,0,i,0] : r-row(i)=0 and b-cb(i)=0}");
   expected_results.push_back("{[0,b,0,r,0,i1,0]->[0,i,0] : r-row(i)=0 and i-i1=0 and b-cb(i)=0}");

   inverse_rels.push_back("{[0,i,0]->[0,b,0,r,0,i,0] : r-row(i)=0 and b-cb(i)=0}");
   expected_results.push_back("{[0,b,0,r,0,i1,0]->[0,i,0] : r-row(i)=0 and i-i1=0 and b-cb(i)=0}");

   inverse_rels.push_back("{[0,i,0]->[0,b,0,r,0,i,0] : r-row(i)=0 and b-cb(i)=0}");
   expected_results.push_back("{[0,b,0,r,0,i1,0]->[0,i,0] : r-row(i)=0 and i-i1=0 and b-cb(i)=0}");

   inverse_rels.push_back("{[b,r,i]->[0,b,0,r,0,i,0]}");
   expected_results.push_back("{[0,b,0,r,0,i,0]->[b,r,i]}");

   inverse_rels.push_back("{[0,b,0,r,0,i0,0]->[ar] : r-row(i0)=0 and b-cb(i0)=0 and ar-row(i0)=0}");
   expected_results.push_back("{[ar]->[0,b,0,r,0,i0,0] : r-row(i0)=0 and b-cb(i0)=0 and ar-row(i0)=0}");

   inverse_rels.push_back("{[ar]->[b,r,i0] : r-row(i0)=0 and b-cb(i0)=0 and ar-row(i0)=0}");
   expected_results.push_back("{[b,r,i0]->[ar] : r-row(i0)=0 and b-cb(i0)=0 and ar-row(i0)=0}");

   inverse_rels.push_back("[ncb,ncols,nnz,nrows,nz]->{[ar]->[b,r,i0] : r-row(ar)=0 and b-cb(ar)=0 and ar-i0=0}");
   expected_results.push_back("[ncb,ncols,nnz,nrows,nz]->{[b,r,i0]->[ar] : r-row(ar)=0 and b-cb(ar)=0 and ar-i0=0}");

   inverse_rels.push_back("{[ar]->[b,r,i0] : r-row(i0)=0 and b-cb(i0)=0 and ar-col(i0)=0}");
   expected_results.push_back("{[b,r,i0]->[ar] : r-row(i0)=0 and b-cb(i0)=0 and ar-col(i0)=0}");

   inverse_rels.push_back("{[b,r]->[b,r,i0]}");
   expected_results.push_back("{[b,r,i0]->[b,r]}");

   inverse_rels.push_back("{[0,b,0,r,0,i0,0]->[ar] : r-row(i0)=0 and b-cb(i0)=0 and ar-i0=0}");
   expected_results.push_back("{[ar]->[0,b,0,r,0,i0,0] : r-row(i0)=0 and b-cb(i0)=0 and ar-i0=0}");

   inverse_rels.push_back("{[0,b,0,r,0,i0,0]->[ar] : r-row(i0)=0 and b-cb(i0)=0 and ar-col(i0)=0}");
   expected_results.push_back("{[ar]->[0,b,0,r,0,i0,0] : r-row(i0)=0 and b-cb(i0)=0 and ar-col(i0)=0}");

   inverse_rels.push_back("{[0,b,0,r,0]->[0,b,0,r,0,i,0]}");
   expected_results.push_back("{[0,b,0,r,0,i,0]->[0,b,0,r,0]}");

   //Make sure there are equal numbers of inputs
   ASSERT_EQ(inverse_rels.size(),expected_results.size());

   for(int i=0; i<inverse_rels.size(); i++) {
      Relation inverse_rel(inverse_rels[i]);
      Relation result(inverse_rel.Inverse());
      Relation expected_result(expected_results[i]);

      EXPECT_EQ(result,expected_result) <<
         inverse_rel.get_isl() << ".Inverse()" << endl <<
         "!=" << expected_result.get_isl();
   }
}

TEST(SPMVRealTest,compose_tests){
   //These tests were taken from a full run of the Python version of IEGen using
   //the spmv computation applying transformations up to sparse loop
   vector<string> compose_in1s,compose_in2s,expected_results;

   compose_in1s.push_back("{[i]->[ar] : ar-row(i)=0}");
   compose_in2s.push_back("{[0,i,0]->[i]}");
   expected_results.push_back("{[0,i,0]->[ar] : ar-row(i)=0}");

   compose_in1s.push_back("{[i]->[ar] : ar-i=0}");
   compose_in2s.push_back("{[0,i,0]->[i]}");
   expected_results.push_back("{[0,i,0]->[ar] : ar-i=0}");

   compose_in1s.push_back("{[i]->[ar] : ar-col(i)=0}");
   compose_in2s.push_back("{[0,i,0]->[i]}");
   expected_results.push_back("{[0,i,0]->[ar] : ar-col(i)=0}");

   compose_in1s.push_back("{[0,i,0]->[0,b,0,r,0,i,0] : r-row(i)=0 and b-cb(i)=0}");
   compose_in2s.push_back("{[i]->[0,i,0]}");
   expected_results.push_back("{[i]->[0,b,0,r,0,i0,0] : r-row(i0)=0 and b-cb(i0)=0 and i-i0=0}");

   compose_in1s.push_back("{[0,i0,0]->[ar] : ar-row(i0)=0}");
   compose_in2s.push_back("{[0,b,0,r,0,i,0]->[0,i,0] : r-row(i)=0 and b-cb(i)=0}");
   expected_results.push_back("{[0,b,0,r,0,i0,0]->[ar] : r-row(i0)=0 and b-cb(i0)=0 and ar-row(i0)=0}");

   compose_in1s.push_back("{[0,i0,0]->[ar] : ar-i0=0}");
   compose_in2s.push_back("{[0,b,0,r,0,i,0]->[0,i,0] : r-row(i)=0 and b-cb(i)=0}");
   expected_results.push_back("{[0,b,0,r,0,i0,0]->[ar] : r-row(i0)=0 and b-cb(i0)=0 and ar-i0=0}");

   compose_in1s.push_back("{[0,i0,0]->[ar] : ar-col(i0)=0}");
   compose_in2s.push_back("{[0,b,0,r,0,i,0]->[0,i,0] : r-row(i)=0 and b-cb(i)=0}");
   expected_results.push_back("{[0,b,0,r,0,i0,0]->[ar] : r-row(i0)=0 and b-cb(i0)=0 and ar-col(i0)=0}");

   compose_in1s.push_back("{[0,b,0,r,0,i,0]->[b,r,i]}");
   compose_in2s.push_back("{[ar]->[0,b,0,r,0,i,0] : r-row(i)=0 and b-cb(i)=0 and ar-row(i)=0}");
   expected_results.push_back("{[ar]->[b,r,i] : r-row(i)=0 and b-cb(i)=0 and ar-row(i)=0}");

   compose_in1s.push_back("{[0,b,0,r,0,i,0]->[b,r,i]}");
   compose_in2s.push_back("{[ar]->[0,b,0,r,0,i,0] : r-row(i)=0 and b-cb(i)=0 and ar-i=0}");
   expected_results.push_back("{[ar]->[b,r,i] : r-row(i)=0 and b-cb(i)=0 and ar-i=0}");

   compose_in1s.push_back("{[0,b,0,r,0,i,0]->[b,r,i]}");
   compose_in2s.push_back("{[ar]->[0,b,0,r,0,i,0] : r-row(i)=0 and b-cb(i)=0 and ar-col(i)=0}");
   expected_results.push_back("{[ar]->[b,r,i] : r-row(i)=0 and b-cb(i)=0 and ar-col(i)=0}");

   //Make sure there are equal numbers of inputs
   ASSERT_EQ(compose_in1s.size(),compose_in2s.size());
   ASSERT_EQ(compose_in2s.size(),expected_results.size());

   for(int i=0; i<compose_in1s.size(); i++) {
      Relation compose_in1(compose_in1s[i]);
      Relation compose_in2(compose_in2s[i]);
      Relation result(compose_in1.Compose(compose_in2));
      Relation expected_result(expected_results[i]);

     EXPECT_EQ(result,expected_result) <<
         compose_in1.get_isl() << endl << ".Compose(" << compose_in2.get_isl() <<
         ") !=" << expected_result.get_isl();
   }
}
